#include "SWOSolver.h"

namespace Solver
{

SWOSolver::SWOSolver(bool forceUpdate):BaseSolver(SolverType_E::SWO_E, "SWO"), forceUpdate_(forceUpdate), vTR_(0.3), vA_(0), vC_(0), vW_(0), vSWONorm01_{0,1}
{
    
}

SWOSolver::~SWOSolver()
{

}

void SWOSolver::solveByIter()
{
    //1.
    preParams();

    //2.
    if (getRandUniform01() < vTR_)
    {
        searchAgents();
    }
    else
    {
        searchAgents2();
    }
}

void SWOSolver::preParams()
{
    //1.
    vW_ = static_cast<value_t>(condition_.limitIter - condition_.currentInter) / condition_.limitIter;
    vA_ = 2 * vW_;
    vC_ = -static_cast<value_t>(condition_.limitIter + condition_.currentInter) / condition_.limitIter;

    //2.
    if (v1_.size() != vecMin_.size())
    {
        v1_.resize(vecMin_.size());
        v2_.resize(vecMin_.size());
    }

    //3.
    shuffleVarIndex();
}

void SWOSolver::searchAgents()
{
    std::size_t i  = 1;
    std::size_t ks =(std::size_t) (vW_ * vecVariable_.size());
    const std::size_t num = vecMin_.size();
    for (auto &obj : vecVariable_)
    {
        auto& x  = obj->opt->x;
        auto& x1 = obj->current->x;

        value_t r1 = getRandUniform01();
        value_t r2 = getRandUniform01();
        value_t r3 = getRandUniform01();
        value_t p  = getRandUniform01();

        value_t vC = vA_ * (2 * r1 - 1);
        value_t l  = (vC_ - 1) * vSWONorm01_(gen_) + 1;
        if (i < ks)
        {
            if (p < vW_)
            {
                if (r1 < r2)
                {
                    value_t m1 = std::fabs(vSWONorm01_(gen_)) * r1;
                    auto &y0 = vecVariable_[vecVarIndex_[0]]->current->x;
                    auto &y1 = vecVariable_[vecVarIndex_[1]]->current->x;
                    for (std::size_t j = 0; j != num; ++j)
                    {
                        x[j] = x1[j] + m1 * (y0[j] - y1[j]);
                    }
                }
                else
                {
                    value_t m2 = 1 / (1 + std::exp(l)) * std::cos(2 * M_PI * l);
                    auto &y = vecVariable_[vecVarIndex_[i]]->current->x;
                    for (std::size_t j = 0; j != num; ++j)
                    {
                        x[j] = y[j] + m2 * (vecMin_[j] + getRandUniform01() * (vecMax_[j] - vecMin_[j]));
                    }
                }
            }
            else
            {
                if (r1 < r2)
                {
                    auto &y = vecVariable_[vecVarIndex_[2]]->current->x;
                    for (std::size_t j = 0; j != num; ++j)
                    {
                        x[j] = x1[j] + vC * std::fabs(2 * getRandUniform01() * y[j] - x[j]);
                    }
                }
                else
                {
                    for (std::size_t j = 0; j != num; ++j)
                    {
                        x[j] = x1[j] + vC * getRandUniform01() * vW_ * 2 - vW_;
                    }
                }
            }
        }
        else
        {
            if (r1 < r2)
            {
                value_t mul = std::cos(2 * l * M_PI);
                auto &x2 = bestVar_->best->x;
                for (std::size_t j = 0; j != num; ++j)
                {
                    x[j] = x2[j] + mul * (x2[j] - x1[j]);
                }
            }
            else
            {
                auto &y0 = vecVariable_[vecVarIndex_[0]]->current->x;
                auto &y1 = vecVariable_[vecVarIndex_[1]]->current->x;
                auto &y2 = vecVariable_[vecVarIndex_[2]]->current->x;
                double_t mul = r3 * std::fabs(calcLevy());
                for (std::size_t j = 0; j != num; ++j)
                {
                    x[j] = y0[j] + mul * (y0[j] - x1[j]);
                    if (getRandUniform01() > 0.5)
                    {
                        x[j] += (1 - r3) * (y2[j] - y1[j]);
                    }
                }
            }
        }

        normVar(x, x1);
        obj->opt->y = fun_->calcFun(x);
        updateVariable(obj, forceUpdate_);
        ++i;
    }
}

void SWOSolver::searchAgents2()
{
    constexpr value_t vCr = 0.2;
    const std::size_t num = vecMin_.size();
    for (auto &obj : vecVariable_)
    {
        value_t l    = (vC_ - 1) * getRandUniform01() + 1;
        value_t expl = std::exp(l);
        //1.
        {
            auto &a = vecVariable_[vecVarIndex_[0]];
            auto& x = obj->current->x;
            auto& y = a->current->x;
            if (a->best->y < obj->best->y)
            {
                for (std::size_t i = 0; i != num; ++i)
                {
                    v1_[i] = y[i] - x[i];
                }
            }
            else
            {
                for (std::size_t i = 0; i != num; ++i)
                {
                    v1_[i] = x[i] - y[i];
                }
            }
        }

        //2.
        {
            auto &a = vecVariable_[vecVarIndex_[1]];
            auto &b = vecVariable_[vecVarIndex_[2]];
            auto& x = a->current->x;
            auto& y = b->current->x;
            if (a->best->y < b->best->y)
            {
                for (std::size_t i = 0; i != num; ++i)
                {
                    v2_[i] = x[i] - y[i];
                }
            }
            else
            {
                for (std::size_t i = 0; i != num; ++i)
                {
                    v2_[i] = y[i] - x[i];
                }
            }
        }
        
        //3.
        {
            auto& x0     = obj->current->x;
            auto& x      = obj->opt->x;
            value_t mul  = expl * std::fabs(vSWONorm01_(gen_));
            value_t mul2 = (1 - expl) * std::fabs(vSWONorm01_(gen_));
            for (std::size_t i = 0; i != num; ++i)
            {
                if (getRandUniform01() < vCr)
                {
                    x[i] = x0[i] + mul * v1_[i] + mul2 * v2_[i];
                }
                else
                {
                    x[i] = x0[i];
                }
            }

            normVar(x, x0);
            obj->opt->y = fun_->calcFun(x);
            updateVariable(obj, forceUpdate_);
        }
    }
}

}